Tianhao Ma, Yingzhe Zhang
python2.7 app_csp1.pyhttp://127.0.0.1:8080/ python2.7 app_csp2.pyhttp://127.0.0.1:8080/python2.7 app.pyhttp://127.0.0.1:8080/csp1http://127.0.0.1:8080/csp2python2.7 app.pyhttp://127.0.0.1:8080/csp1#1http://127.0.0.1:8080/csp2#1python2.7 app.pyhttp://127.0.0.1:8080/python2.7 app.pyhttp://127.0.0.1:8080/python2.7 app.pyhttp://127.0.0.1:8080/csp1http://127.0.0.1:8080/csp2(1) where the vulnerable source code locates:
we have a input:

Then it is expected that a text should be typed here and the website will respond the text to user as follow:

But what if we input javascript code instead of plain text? we can see the code:

This is because the original code does not filter the javascript code and allow the javascript inject here and execute when it return to the html, which causes injection vulnerability:

(2) how to trigger vulnerability:
So if we use: <script>alert("Hello world!!!")</script>here, the alert will appear here:

(1) Where the vulnerable source code locates:
We can input some text in the text box and then the text will be shown in the post list.

First we try to directly input the <script>alert("Hello World")</script> in the text box. Then the new post is shown but the alert not. So we go into the source code and I find the following sentence.

post[i].message is the text of our input and it will be added to the html. Finally, the html will be added to containerEl.innerHTML. If the script is dynamically embedded by innerHTML, the browser will treat it as normal text and will not maintain it as a script node in the DOM. So it cannot be found when called.
(2) How to trigger vulnerability:
There is an easy way. Since the data is stored on the server side. We can use the onerror event, and the function that handles the error will be called. In this way, we can put the alert into the function. For instance, we can write a post like this:
<img src="cannot_find_img.png" onerror=alert("Yingzhe")>.
The server cannot find the image so there will be an error and the onerror function will be called. Then we can see the alert.

(1) where the vulnerable source code locates:
we have a website which contains three tab that users can click and show the picture below it:

and we can check the code from console of browser and find the number in url after # is showed here:

So if we put some text in url instead of number, we will find:

This is because the original code directly let the text execute in the code rather than filter it:

(2) how to trigger vulnerability:
So if we put some javascript in Url after #:

and the code will be interpreted here:

and this will make alert pops up:

(1) Where the vulnerable source code locates:
We can input a number to the timer and then the timer start. At the same time we can see the number is added to the parameters in the URL.


Let's go into the source code. On the python side, our input will be saved in the timer and then the timer.html will be rendered using the timer.

And in the timer.html, the startTimer() function will be called when the page is loading. As we can see, '{{ timer }}' is the parameter and we can treat that as a string.

(2) How to trigger vulnerability:
Since we can treat the timer parameter as a string, we can include') in our input to end the startTimer() first and then add the alert . Then whole input looks like this:
3');alert('Yingzhe.
When the page loading, the following will be called. startTimer(3); alert('Yingzhe');. So we can see the alert.

(1) where the vulnerable source code locates:
when we first get into website, we find this:

This is because:

when we click this link, we are brought to here:

and the code will get the value of next which is comfirm and set this value to next,then render the html signup.html.

when we get into the signup.html, the nextvalue will be set here. So what if we directly type the url https://xss-game.appspot.com/level5/frame/signup?next=mario and set next=mario, we can get:
(2) how to trigger vulnerability:
So if we inject the code like this https://xss-game.appspot.com/level5/frame/signup?next=javascript:alert('mario'), the alert will pop up:

(1) Where the vulnerable source code locates:
We can change the string after # in the URL. If we change that to www.google.com, we can see the result in the page.

So it looks like the page will load the URL. Let's take a look at the source code.

The function will create a <script> element and put the content of the URL into the <script>.
(2) How to trigger vulnerability:
Actually we can write the alert() into a JavaScript file but there is a easy way: use the Data URLs
Data URLs
URLs prefixed with the
data:scheme, allow content creators to embed small files inline in documents.data:[<mediatype>][;base64],<data>
So we can input the URL like this:
https://xss-game.appspot.com/level6/frame#data:text/plain,alert("Yingzhe")
<mediatype> is text/plain and <data> is alert("Yingzhe").
So we can see the alert.

Since regular expressions are case-sensitive, we can also type that http as HTtp to bypass the check. Or type a space' ' before the http.
Since we can input any text and may interpret them as javascript code, which will bring injection vulnerability, I write a method named 'escapeHtml' which uses the dependency cgi to do the string escape. Finally can interpret the code as normal text even the input string is javascript code.

First we create a server using python flask. It will send the CSP header to the client(in next step of the home work) and render the html. In the JavaScript side, we add a encodeText() function to encode the input as the plain text.

The logic is creating a <div> and putting the text in its innerText (for Firefox is textContent). And then we extract the innerHTML of the div as the plain text. Before we put the input of the user to the html, we encode the post[].message as plain text. The page finally displays the original text typed by the user.

Cause the first post is a Welcome Message , we will skip it and then encode the following post. In this case, the result will be

Since the attack happens in the client-end, we just fix that in html.

As the picture shows that there are two ways to defend injection. The first is that write a function named 'html2Escape' to escape the text from html input, which supports the escaping as follow:
| Code | Escape |
|---|---|
| < | < |
| > | > |
| & | & |
| " | " |
Then if we input javascript code ><img src=x onerror="alert("mario")", the function help us interpret it as ><img src=x onerror="alert("mario")", which will prevent injection vulnerability.
Then let us talk about other way. Through analysing the code, we know the function receives an argument whose type is a number(int). So we can first transfer the argument and parse it into an integer,then uses isNaN to judge if the result is an integer. If we get the result is True, we directly end the method. For example, if we set the argument number='mario', the parseInt method produce NaN with this argument. Then we judge the NaN and get the result True and end the process.
By the way, after moving the javascript code into a js file, we need import it into html.

This time we will check the user's input on the sever side. We create a check_timer() function to get the submission value of the timer and check if it is a number using isdigit(). The sever will return the right page only when the user input a valid value.

On the client side, the only change is that we add the check_timer as a GET mothed to the action in the <form> element. The client will send the request including the timer value to the sever.

In this case, if the input is not a number, the sever will return a error page to the client.

When we download the source code and run it locally, we find if we put the JavaScript code in the<head> the selector cannot find the element with its id because the page has not finished loading. So we put the code of selector in the <body>. And in the next CSP step, we put that in a JS file.

Since Using the javascript:alert('mario') and http:// can cause the vulnerability, I decide to filter this two type request.

First by checking if javascript exists in request path and using regular expression to judge the malicious code, we can detect the injection vulnerability. In this circumstance, I direct the request to the page hacker_fail.html, which effectively prevent injection vulnerability.
However, I this solution is very tricky. If we are sure that the next step is going to "confirm.html", we can write this hard code to make sure we always get the right path no matter what the input arguments are(I state this circumstance in comment).
Due to the protocol limitation, the content behind the # of the URL cannot be included in the request, so we cannot check the input in the address bar on the server side. So the main problem is to modify the regular expression part.
When we get the content after #, we will delete all the space ' ' in that using Regex.

Double backslash symbol // is another way to use https or http, so the next step is check wether the content starting with //.

After that, get the first 8 character of the content as the urlHead and transfer that to lowercase. In these way, we can check if the url uses HTTP, data url, FTP, SMTP and other protocols. Once it is detected that it uses the above protocol, an error message will be displayed.

The result is as follows,

csp_json = {
"script-src": "https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js 'self'",
"img-src": "https://xss-game.appspot.com/static/logos/level1.png",
"default-src": "self",
"style-src": "https://xss-game.appspot.com/static/game-frame-styles.css",
}
use sha256 to handle inline js code
csp_json = {
"script-src": "https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js "
"'sha256-h6wTjHUH5feO5x5t9UyBsoZr6dMUtn9mb9wHpKUf7Uw='",
"img-src": "https://xss-game.appspot.com/static/logos/level1.png",
"default-src": "self",
"style-src": "https://xss-game.appspot.com/static/game-frame-styles.css",
}
@csp_header({
"default-src": "'self'",
"script-src": "'self' https://xss-game.appspot.com/static/post-store.js",
"img-src": "https://xss-game.appspot.com/static/logos/level2.png "
"https://xss-game.appspot.com/static/level2_icon.png "
"https://ssl.gstatic.com/s2/oz/images/sprites/stream-e001443aa61c5529c1aa133a9c12bb49.png",
"style-src": "https://xss-game.appspot.com/static/game-frame-styles.css",
"report-uri": ""
})
use nonce to handle inline js code
csp_json = {
"default-src": "'self'",
'script-src': "'self' https://xss-game.appspot.com/static/post-store.js 'nonce-" + nonce + "'",
"img-src": "https://xss-game.appspot.com/static/logos/level2.png "
"https://xss-game.appspot.com/static/level2_icon.png "
"https://ssl.gstatic.com/s2/oz/images/sprites/stream-e001443aa61c5529c1aa133a9c12bb49.png",
"style-src": "https://xss-game.appspot.com/static/game-frame-styles.css",
"report-uri": ""
}
The function of generating random nonce is
import hashlib
m = hashlib.sha256("hello")
# generate random nonce
def generate_nonce():
global m
m.update("hello world")
return m.hexdigest()
@csp_header({'script-src': "'self' http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js",
"img-src": "https://xss-game.appspot.com/static/logos/level3.png "
"https://xss-game.appspot.com/static/level3/cloud1.jpg "
"https://xss-game.appspot.com/static/level3/cloud2.jpg "
"https://xss-game.appspot.com/static/level3/cloud3.jpg",
"style-src": "https://xss-game.appspot.com/static/game-frame-styles.css",
"report-uri": ""})
use random nonce- to handle inline js code.
csp_json = {'script-src': "'self' http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js "
"'nonce-" + nonce + "'",
"img-src": "https://xss-game.appspot.com/static/logos/level3.png "
"https://xss-game.appspot.com/static/level3/cloud1.jpg "
"https://xss-game.appspot.com/static/level3/cloud2.jpg "
"https://xss-game.appspot.com/static/level3/cloud3.jpg",
"style-src": "https://xss-game.appspot.com/static/game-frame-styles.css",
"report-uri": ""
}
the method of generating random nonce
import hashlib
m = hashlib.sha256("hello")
# generate random nonce
def generate_nonce():
global m
m.update("hello world")
return m.hexdigest()
nonce = generate_nonce()
@csp_header({
"default-src": "'self'",
"script-src": "'self' https://xss-game.appspot.com/static/game-frame.js",
"img-src": "https://xss-game.appspot.com/static/logos/level4.png https://xss-game.appspot.com/static/loading.gif",
"style-src": "https://xss-game.appspot.com/static/game-frame-styles.css",
"report-uri": ""
})
use random nonce- to handle inline js code.
csp_json = {"default-src": "'self'",
"script-src": "'self' https://xss-game.appspot.com/static/game-frame.js 'nonce-" + nonce + "'",
"img-src": "https://xss-game.appspot.com/static/logos/level4.png "
"https://xss-game.appspot.com/static/loading.gif",
"style-src": "https://xss-game.appspot.com/static/game-frame-styles.css",
"report-uri": ""
}
the method of generating random nonce
import hashlib
m = hashlib.sha256("hello")
# generate random nonce
def generate_nonce():
global m
m.update("hello world")
return m.hexdigest()
csp_json = {
"script-src": "'self' http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js ",
"img-src": "https://xss-game.appspot.com/static/logos/level5.png ",
"default-src": "self",
"style-src": "https://xss-game.appspot.com/static/game-frame-styles.css",
"report-uri": "",
}
use sha256 to handle inline js code
csp_json = {
"script-src": "'self' http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js "
"'sha256-cN25VmyGJVDLdpkS+JoZ3jMxtke1QdPN8mucaJj/2bc='",
"img-src": "https://xss-game.appspot.com/static/logos/level5.png ",
"default-src": "self",
"style-src": "https://xss-game.appspot.com/static/game-frame-styles.css",
"report-uri": "",
}
@csp_header({
"default-src": "'self'",
"script-src": "'self' https://xss-game.appspot.com/static/game-frame.js",
"img-src": "https://xss-game.appspot.com/static/level6_cube.png "
"https://xss-game.appspot.com/static/logos/level6.png ",
"style-src": "https://xss-game.appspot.com/static/game-frame-styles.css",
"report-uri": ""
})
use nonce to handle inline js code.
csp_json = {"default-src": "'self'",
"script-src": "'self' https://xss-game.appspot.com/static/game-frame.js 'nonce-" + nonce + "'",
"img-src": "https://xss-game.appspot.com/static/level6_cube.png "
"https://xss-game.appspot.com/static/logos/level6.png ",
"style-src": "https://xss-game.appspot.com/static/game-frame-styles.css",
"report-uri": ""
}
the method of generating random nonce
import hashlib
m = hashlib.sha256("hello")
# generate random nonce
def generate_nonce():
global m
m.update("hello world")
return m.hexdigest()